// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

.intel_syntax noprefix
#include "unixasmmacros.inc"
#include "asmconstants.h"


//////////////////////////////////////////////////////////////////////////
//
// PrecodeFixupThunk
//
// The call in fixup precode initally points to this function.
// The pupose of this function is to load the MethodDesc and forward the call the prestub.
//
// EXTERN_C VOID __stdcall PrecodeFixupThunk()
LEAF_ENTRY PrecodeFixupThunk, _TEXT

        pop     rax         // Pop the return address. It points right after the call instruction in the precode.

        // Inline computation done by FixupPrecode::GetMethodDesc()
        movzx   r10,byte ptr [rax+2]    // m_PrecodeChunkIndex
        movzx   r11,byte ptr [rax+1]    // m_MethodDescChunkIndex
        mov     rax,qword ptr [rax+r10*8+3]
        lea     METHODDESC_REGISTER,[rax+r11*8]

        // Tail call to prestub
        jmp C_FUNC(ThePreStub)

LEAF_END PrecodeFixupThunk, _TEXT

// EXTERN_C int __fastcall HelperMethodFrameRestoreState(
//         INDEBUG_COMMA(HelperMethodFrame *pFrame)
//         MachState *pState
//         )
LEAF_ENTRY HelperMethodFrameRestoreState, _TEXT

#ifdef _DEBUG
        mov     rdi, rsi
#endif

        // Check if the MachState is valid
        xor     eax, eax
        cmp     qword ptr [rdi + OFFSETOF__MachState___pRetAddr], rax
        jne      DoRestore
        REPRET
DoRestore:

        //
        // If a preserved register were pushed onto the stack between
        // the managed caller and the H_M_F, m_pReg will point to its
        // location on the stack and it would have been updated on the
        // stack by the GC already and it will be popped back into the
        // appropriate register when the appropriate epilog is run.
        // 
        // Otherwise, the register is preserved across all the code
        // in this HCALL or FCALL, so we need to update those registers
        // here because the GC will have updated our copies in the 
        // frame.
        //
        // So, if m_pReg points into the MachState, we need to update
        // the register here.  That's what this macro does.
        //
#define RestoreReg(reg, regnum) \
        lea     rax, [rdi + OFFSETOF__MachState__m_Capture + 8 * regnum]; \
        mov     rdx, [rdi + OFFSETOF__MachState__m_Ptrs + 8 * regnum]; \
        cmp     rax, rdx; \
        cmove   reg, [rax];

        // regnum has to match ENUM_CALLEE_SAVED_REGISTERS macro
        RestoreReg(R12, 0)
        RestoreReg(R13, 1)
        RestoreReg(R14, 2)
        RestoreReg(R15, 3)
        RestoreReg(Rbx, 4)
        RestoreReg(Rbp, 5)

        xor     eax, eax
        ret

LEAF_END HelperMethodFrameRestoreState, _TEXT

//////////////////////////////////////////////////////////////////////////
//
// NDirectImportThunk
//
// In addition to being called by the EE, this function can be called
//  directly from code generated by JIT64 for CRT optimized direct
//  P/Invoke calls. If it is modified, the JIT64 compiler's code
//  generation will need to altered accordingly.
//
// EXTERN_C VOID __stdcall NDirectImportThunk()//
NESTED_ENTRY NDirectImportThunk, _TEXT, NoHandler

        //
        // Save integer parameter registers.
        // Make sure to preserve r11 as well as it is used to pass the stack argument size from JIT
        //
        PUSH_ARGUMENT_REGISTERS
        push_register r11
        
        //
        // Allocate space for XMM parameter registers
        //
        alloc_stack     0x80 

        SAVE_FLOAT_ARGUMENT_REGISTERS 0

    END_PROLOGUE

        //
        // Call NDirectImportWorker w/ the NDirectMethodDesc*
        //
        mov             rdi, METHODDESC_REGISTER
        call            C_FUNC(NDirectImportWorker)
        
        RESTORE_FLOAT_ARGUMENT_REGISTERS 0

        //
        // epilogue, rax contains the native target address
        //
        free_stack      0x80

        //
        // Restore integer parameter registers and r11
        //
        pop_register r11
        POP_ARGUMENT_REGISTERS
        
    TAILJMP_RAX
NESTED_END NDirectImportThunk, _TEXT

// EXTERN_C void moveOWord(LPVOID* src, LPVOID* target);
// <NOTE>
// MOVDQA is not an atomic operation.  You need to call this function in a crst.
// </NOTE>
LEAF_ENTRY moveOWord, _TEXT
        movdqu          xmm0, xmmword ptr [rdi]
        movdqu          xmmword ptr [rsi], xmm0

        ret
LEAF_END moveOWord, _TEXT

//------------------------------------------------
// JIT_RareDisableHelper
//
// The JIT expects this helper to preserve registers used for return values
//
NESTED_ENTRY JIT_RareDisableHelper, _TEXT, NoHandler

    // First integer return register
    push_register rax
    // Second integer return register
    push_register rdx
    alloc_stack         0x28
    END_PROLOGUE
    // First float return register
    movdqa              xmmword ptr [rsp], xmm0
    // Second float return register
    movdqa              xmmword ptr [rsp+0x10], xmm1

    call                C_FUNC(JIT_RareDisableHelperWorker)

    movdqa              xmm0, xmmword ptr [rsp]
    movdqa              xmm1, xmmword ptr [rsp+0x10]
    free_stack          0x28
    pop_register        rdx
    pop_register        rax
    ret

NESTED_END JIT_RareDisableHelper, _TEXT

#ifdef FEATURE_HIJACK

//------------------------------------------------
// OnHijackTripThread
//
NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler

    // Make room for the real return address (rip)
    push_register rax

    PUSH_CALLEE_SAVED_REGISTERS

    push_register rdx
    // Push rax again - this is where integer/pointer return values are returned
    push_register rax

    mov                 rdi, rsp

    alloc_stack         0x28

    // First float return register
    movdqa              [rsp], xmm0
    // Second float return register
    movdqa              [rsp+0x10], xmm1

    END_PROLOGUE

    call                C_FUNC(OnHijackWorker)

    movdqa              xmm0, [rsp]
    movdqa              xmm1, [rsp+0x10]
    free_stack          0x28
    pop_register        rax
    pop_register        rdx

    POP_CALLEE_SAVED_REGISTERS
    ret

NESTED_END OnHijackTripThread, _TEXT

#endif // FEATURE_HIJACK

LEAF_ENTRY SinglecastDelegateInvokeStub, _TEXT                                        
                                                                                      
        test    rdi, rdi                                                              
        jz      NullObject                                                            
                                                                                      
                                                                                      
        mov     rax, [rdi + OFFSETOF__DelegateObject___methodPtr]                     
        mov     rdi, [rdi + OFFSETOF__DelegateObject___target]  // replace "this" pointer
                                                                                      
        jmp     rax                                                                   
                                                                                      
NullObject:                                                                           
        mov     rdi, CORINFO_NullReferenceException_ASM                               
        jmp     C_FUNC(JIT_InternalThrow)
                                                                                      
LEAF_END SinglecastDelegateInvokeStub, _TEXT
